/*
 *  ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
 *  numeric.c: Numeric handling functions.
 *
 *  Copyright (C) 2002 by the past and present ircd coders, and others.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 *  USA
 *
 *  $Id: numeric.c 147 2005-12-26 02:52:10Z jon $
 */

#include "stdinc.h"

#include "numeric.h"
#include "irc_string.h"
#include "common.h"		/* NULL cripes */
#include "memory.h"
#include "s_log.h"
#include "fileio.h"
#include "send.h"
#include "client.h"
#include "messages.tab"

static char used_locale[LOCALE_LENGTH] = "standard";

/*
 * form_str
 *
 * inputs	- numeric
 * output	- corresponding string
 * side effects	- NONE
 */
const char *
form_str(int numeric)
{
	assert(-1 < numeric);
	assert(numeric < ERR_LAST_ERR_MSG);

	if(numeric > ERR_LAST_ERR_MSG)
		numeric = ERR_LAST_ERR_MSG;
	if(numeric < 0)
		numeric = ERR_LAST_ERR_MSG;

	assert(replies[numeric].standard != NULL);

	return (replies[numeric].translated != NULL ? replies[numeric].translated :
		replies[numeric].standard);
}

/* Attempts to change a numeric with index "reply" to "new_reply".
 * Returns 1 if ok, 0 otherwise.
 */
static int
change_reply(const char *locale, int linecnt, int reply, char *new_reply)
{
	int found;
	char *new = new_reply;
	const char *old = replies[reply].standard;

	for(; *new; new++)
	{
		if(*new == '%')
		{
			if(!*++new)
				break;
			if(*new != '%')
			{
				/* We've just found a format symbol. Check if it is the next format
				 * symbol in the original reply.
				 */
				for(; *new >= '0' && *new <= '9'; new++);	/* skip size prefix */
				found = 0;
				for(; *old; old++)
				{
					if(*old == '%')
					{
						if(!*++old)
							break;	/* shouldn't happen */
						if(*old != '%')
						{
							for(; *old >= '0' && *old <= '9'; old++);	/* skip size prefix */
							if(*new != *old++)
							{
								ilog(L_ERROR,
								     "Incompatible format symbols (%s.lang, %d)",
								     locale, linecnt);
								return 0;
							}
							found = 1;
							break;
						}
					}
				}
				if(!found)
				{
					ilog(L_ERROR, "Too many format symbols (%s.lang, %d)",
					     locale, linecnt);
					return (0);
				}
			}
		}
	}

	MyFree(replies[reply].translated);
	DupString(replies[reply].translated, new_reply);
	return (1);
}

/* Loads a language file. Errors are logged into the log file. */
void
set_locale(const char *locale)
{
	int i, res = 1, linecnt = 0;
	char buffer[IRCD_BUFSIZE + 1];
	char *ident, *reply;
	FBFILE *f;

	/* Restore standard replies */
	for(i = 0; i <= ERR_LAST_ERR_MSG; i++)	/* 0 isn't a magic number! ;> */
	{
		if(replies[i].translated != NULL)
		{
			MyFree(replies[i].translated);
			replies[i].translated = NULL;
		}
	}

	if(strchr(locale, '/') != NULL)
	{
		strlcpy(used_locale, "standard", sizeof(used_locale));	/* XXX paranoid */
		return;
	}

	/* yes, I know - the slash isn't necessary. But I have to be sure
	 * that it'll work even if some lame admin won't put "/" at the end
	 * of MSGPATH.
	 */
	snprintf(buffer, sizeof(buffer), "%s/%s.lang", MSGPATH, locale);
	if((f = fbopen(buffer, "r")) == NULL)
	{
		strlcpy(used_locale, "standard", sizeof(used_locale));	/* XXX */
		return;
	}

	/* Process the language file */
	while(fbgets(buffer, sizeof(buffer), f))
	{
		++linecnt;
		if(buffer[0] == ';')
			continue;	/* that's a comment */

		if((ident = strpbrk(buffer, "\r\n")) != NULL)
			*ident = '\0';

		/* skip spaces if there are any */
		for(ident = buffer; *ident == ' ' || *ident == '\t'; ident++) /* null */ ;
		if(*ident == '\0')
			continue;	/* empty line */

		/* skip after the reply identificator */
		for(reply = ident; *reply != ' ' && *reply != '\t' && *reply != ':'; reply++)
			if(*reply == '\0')
				goto error;

		if(*reply == ' ' || *reply == '\t')
		{
			for(*reply++ = '\0'; *reply == ' ' || *reply == '\t'; reply++);
			if(*reply != ':')
			{
			      error:
				ilog(L_ERROR, "Invalid line in language file (%s.lang, %d)",
				     locale, linecnt);
				res = 0;
				continue;
			}
		}
		else
			*reply++ = '\0';
		if(*ident == '\0')
			goto error;

		/* skip to the beginning of reply */
		while(*reply == ' ' || *reply == '\t')
			reply++;
		if(*reply == '\0')
			goto error;

		for(i = 0; i <= ERR_LAST_ERR_MSG; i++)
		{
			if(replies[i].name != NULL)
			{
				if(irccmp(replies[i].name, ident) == 0)
				{
					if(!change_reply(locale, linecnt, i, reply))
						res = 0;
					i = -1;
					break;
				}
			}
		}
		if(i != -1)
		{
			ilog(L_ERROR, "Unknown numeric %s (%s.lang, %d)", ident, locale, linecnt);
			res = 0;
		}
	}
	fbclose(f);

	strlcpy(used_locale, locale, sizeof(used_locale));
	if(!res)
		sendto_realops_flags(UMODE_ALL, L_ADMIN, "Language file [%s] contains "
				     "errors, check server log file for more details", used_locale);
}

/* Returns the name of current locale. */
const char *
get_locale(void)
{
	return used_locale;
}
